// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/ic/ic-stats.h"

#include "src/counters.h"
#include "src/objects-inl.h"
#include "src/tracing/trace-event.h"
#include "src/tracing/traced-value.h"
#include "src/v8.h"

namespace v8 {
namespace internal {

    base::LazyInstance<ICStats>::type ICStats::instance_ = LAZY_INSTANCE_INITIALIZER;

    ICStats::ICStats()
        : ic_infos_(MAX_IC_INFO)
        , pos_(0)
    {
        base::Relaxed_Store(&enabled_, 0);
    }

    void ICStats::Begin()
    {
        if (V8_LIKELY(!TracingFlags::is_ic_stats_enabled()))
            return;
        base::Relaxed_Store(&enabled_, 1);
    }

    void ICStats::End()
    {
        if (base::Relaxed_Load(&enabled_) != 1)
            return;
        ++pos_;
        if (pos_ == MAX_IC_INFO) {
            Dump();
        }
        base::Relaxed_Store(&enabled_, 0);
    }

    void ICStats::Reset()
    {
        for (auto ic_info : ic_infos_) {
            ic_info.Reset();
        }
        pos_ = 0;
    }

    void ICStats::Dump()
    {
        auto value = v8::tracing::TracedValue::Create();
        value->BeginArray("data");
        for (int i = 0; i < pos_; ++i) {
            ic_infos_[i].AppendToTracedValue(value.get());
        }
        value->EndArray();

        TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.ic_stats"), "V8.ICStats",
            TRACE_EVENT_SCOPE_THREAD, "ic-stats", std::move(value));
        Reset();
    }

    const char* ICStats::GetOrCacheScriptName(Script script)
    {
        Address script_ptr = script.ptr();
        if (script_name_map_.find(script_ptr) != script_name_map_.end()) {
            return script_name_map_[script_ptr].get();
        }
        Object script_name_raw = script->name();
        if (script_name_raw->IsString()) {
            String script_name = String::cast(script_name_raw);
            char* c_script_name = script_name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL)
                                      .release();
            script_name_map_.insert(
                std::make_pair(script_ptr, std::unique_ptr<char[]>(c_script_name)));
            return c_script_name;
        } else {
            script_name_map_.insert(
                std::make_pair(script_ptr, std::unique_ptr<char[]>(nullptr)));
            return nullptr;
        }
        return nullptr;
    }

    const char* ICStats::GetOrCacheFunctionName(JSFunction function)
    {
        Address function_ptr = function.ptr();
        if (function_name_map_.find(function_ptr) != function_name_map_.end()) {
            return function_name_map_[function_ptr].get();
        }
        SharedFunctionInfo shared = function->shared();
        ic_infos_[pos_].is_optimized = function->IsOptimized();
        char* function_name = shared->DebugName()->ToCString().release();
        function_name_map_.insert(
            std::make_pair(function_ptr, std::unique_ptr<char[]>(function_name)));
        return function_name;
    }

    ICInfo::ICInfo()
        : function_name(nullptr)
        , script_offset(0)
        , script_name(nullptr)
        , line_num(-1)
        , is_constructor(false)
        , is_optimized(false)
        , map(nullptr)
        , is_dictionary_map(false)
        , number_of_own_descriptors(0)
    {
    }

    void ICInfo::Reset()
    {
        type.clear();
        function_name = nullptr;
        script_offset = 0;
        script_name = nullptr;
        line_num = -1;
        is_constructor = false;
        is_optimized = false;
        state.clear();
        map = nullptr;
        is_dictionary_map = false;
        number_of_own_descriptors = 0;
        instance_type.clear();
    }

    void ICInfo::AppendToTracedValue(v8::tracing::TracedValue* value) const
    {
        value->BeginDictionary();
        value->SetString("type", type);
        if (function_name) {
            value->SetString("functionName", function_name);
            if (is_optimized) {
                value->SetInteger("optimized", is_optimized);
            }
        }
        if (script_offset)
            value->SetInteger("offset", script_offset);
        if (script_name)
            value->SetString("scriptName", script_name);
        if (line_num != -1)
            value->SetInteger("lineNum", line_num);
        if (is_constructor)
            value->SetInteger("constructor", is_constructor);
        if (!state.empty())
            value->SetString("state", state);
        if (map) {
            // V8 cannot represent integer above 2^53 - 1 in JavaScript from JSON,
            // thus `map` should be converted to a string rather than an integer.
            std::stringstream ss;
            ss << map;
            value->SetString("map", ss.str());
        }
        if (map)
            value->SetInteger("dict", is_dictionary_map);
        if (map)
            value->SetInteger("own", number_of_own_descriptors);
        if (!instance_type.empty())
            value->SetString("instanceType", instance_type);
        value->EndDictionary();
    }

} // namespace internal
} // namespace v8
